/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "Attest_spt_fp.h"
#include "Certify_fp.h"

#if CC_Certify  // Conditional expansion of this file

/*(See part 3 specification)
// prove an object with a specific Name is loaded in the TPM
*/
//  Return Type: TPM_RC
//      TPM_RC_KEY          key referenced by 'signHandle' is not a signing key
//      TPM_RC_SCHEME       'inScheme' is not compatible with 'signHandle'
//      TPM_RC_VALUE        digest generated for 'inScheme' is greater or has larger
//                          size than the modulus of 'signHandle', or the buffer for
//                          the result in 'signature' is too small (for an RSA key);
//                          invalid commit status (for an ECC key with a split scheme)
/*
tpm2-tools
tpm2_certify(1) - Proves that an object with a specific NAME is loaded in the TPM. By certifying that the object is loaded,
the TPM warrants that a public area with a given NAME is self-consistent and associated with a valid sensitive area.

tpm2_certify(1) - 证明具有特定名称的对象已加载到 TPM 中。 通过证明对象已加载，TPM 保证具有给定 NAME 的公共区域是自洽的并且与有效的敏感区域相关联。

If a relying party has a public area that has the same NAME as a NAME certified with this command, then the values in that 
public area are correct. An object that only has its public area loaded cannot be certified.

如果依赖方有一个公共区域，其名称与使用此命令认证的 NAME 相同，则该公共区域中的值是正确的。 仅加载其公共区域的对象不能被认证。

*/
/*
Part3, 18 Attestation Commands
For a signing key that is not restricted, the caller may specify the scheme to be used as long as the scheme is compatible 
with the family of the key (for example, TPM_ALG_RSAPSS cannot be selected for an ECC key).
 If the caller sets scheme to TPM_ALG_NULL, then the default scheme of the key is used.
For a restricted signing key, the key's scheme cannot be TPM_ALG_NULL and cannot be overridden.
If the handle for the signing key (signHandle) is TPM_RH_NULL, then all of the actions of the command are performed and the 
attestation block is “signed” with the NULL Signature.

对于不受限制的签名密钥，调用者可以指定要使用的方案，只要该方案与密钥族兼容即可（例如，不能为 ECC 密钥选择 TPM_ALG_RSAPSS）。
如果调用者将方案设置为 TPM_ALG_NULL，则使用密钥的默认方案。
对于受限签名密钥，密钥的方案不能是 TPM_ALG_NULL 并且不能被覆盖。
如果签名密钥的句柄 (signHandle) 是 TPM_RH_NULL，则执行该命令的所有操作，并且使用 NULL 签名“签署”证明块。

NOTE 1 This mechanism is provided so that additional commands are not required to access the data that might be in an attestation structure.
注 1：提供这种机制是为了不需要额外的命令来访问可能位于证明结构中的数据。

Each of the attestation structures contains a TPMS_CLOCK_INFO structure and a firmware version
number. These values may be considered privacy-sensitive, because they would aid in the correlation of
attestations by different keys. To provide improved privacy, the resetCount, restartCount, and
firmwareVersion numbers are obfuscated when the signing key is not in the Endorsement or Platform
hierarchies
每个证明结构都包含一个 TPMS_CLOCK_INFO 结构和一个固件版本号。 这些值可能被认为是隐私敏感的，因为它们将有助于通过不同密钥关联证明。
为了提供更好的隐私，当签名密钥不在背书或平台层次结构中时，会混淆 resetCount、restartCount 和固件版本号

Each of the attestation structures allows the caller to provide some qualifying data (qualifyingData). 
For most signing schemes, this value will be placed in the TPMS_ATTEST.extraData parameter that is then hashed and signed. 
However, for some schemes such as ECDAA, the qualifyingData is used in a different manner (for details, see “ECDAA” in TPM 2.0 Part 1).
每个证明结构都允许调用者提供一些限定数据（qualifyingData）。
对于大多数签名方案，此值将放在 TPMS_ATTEST.extraData 参数中，然后对其进行哈希处理和签名。 然而，对于一些方案，如 ECDAA，合格数据的使用方式不同
（有关详细信息，请参阅 TPM 2.0 第 1 部分中的“ECDAA”）。


Part3, 18.2 TPM2_Certify
The purpose of this command is to prove that an object with a specific Name is loaded in the TPM.
By certifying that the object is loaded, the TPM warrants that a public area with a given Name is selfconsistent and associated with a valid 
sensitive area. If a relying party has a public area that has the same Name as a Name certified with this command, then the values in that public 
area are correct.
此命令的目的是证明具有特定名称的对象已加载到 TPM 中。
通过证明对象已加载，TPM 保证具有给定名称的公共区域是自洽的并且与有效的敏感区域相关联。 如果依赖方有一个公共区域，其名称与使用此命令认证的名称相同，则该公共区域中的值是正确的。

Authorization for objectHandle requires ADMIN role authorization. If performed with a policy session, the session shall have a 
policySession→commandCode set to TPM_CC_Certify. 
This indicates that the policy that is being used is a policy that is for certification, and not a policy that would approve another use. 
That is, authority to use an object does not grant authority to certify the object.
objectHandle 的授权需要 ADMIN 角色授权。 如果与策略会话一起执行，则该会话应将 policySession→commandCode 设置为 TPM_CC_Certify。
这表明正在使用的策略是用于认证的策略，而不是批准其他用途的策略。 也就是说，使用对象的权限并不授予证明该对象的权限。

The object may be any object that is loaded with TPM2_Load() or TPM2_CreatePrimary(). An object that
only has its public area loaded cannot be certified.
该对象可以是使用 TPM2_Load() 或 TPM2_CreatePrimary() 加载的任何对象。 仅加载其公共区域的对象不能被认证。

NOTE 2 The restriction occurs because the Name is used to identify the object being certified. If the TPM
has not validated that the public area is associated with a matched sensitive area, then the public
area may not represent a valid object and cannot be certified.
注 2 出现限制是因为名称用于标识被认证的对象。
  如果 TPM 未验证公共区域与匹配的敏感区域相关联，则公共区域可能不代表有效对象并且无法被认证。

The certification includes the Name and Qualified Name of the certified object as well as the Name and
the Qualified Name of the certifying object.
NOTE 3 If signHandle is TPM_RH_NULL, the TPMS_ATTEST structure is returned and signature is a NULL
Signature
认证包括被认证对象的名称和合格名称，以及认证对象的名称和合格名称。
注 3 如果 signHandle 是 TPM_RH_NULL，则返回 TPMS_ATTEST 结构并且签名是 NULL 签名
*/
TPM_RC
TPM2_Certify(
    Certify_In      *in,            // IN: input parameter list
    Certify_Out     *out            // OUT: output parameter list
    )
{
    TPMS_ATTEST             certifyInfo;
    OBJECT                  *signObject = HandleToObject(in->signHandle);
    OBJECT                  *certifiedObject = HandleToObject(in->objectHandle);
// Input validation
    if(!IsSigningObject(signObject))
        return TPM_RCS_KEY + RC_Certify_signHandle;
    if(!CryptSelectSignScheme(signObject, &in->inScheme))
        return TPM_RCS_SCHEME + RC_Certify_inScheme;

// Command Output
    // Filling in attest information
    // Common fields
    FillInAttestInfo(in->signHandle, &in->inScheme, &in->qualifyingData,
                     &certifyInfo);

    // Certify specific fields
    certifyInfo.type = TPM_ST_ATTEST_CERTIFY;
    // NOTE: the certified object is not allowed to be TPM_ALG_NULL so
    // 'certifiedObject' will never be NULL
    // 被证明对象的对象名
    // TODO: 对象名即对象的hash?
    certifyInfo.attested.certify.name = certifiedObject->name;

    // When using an anonymous signing scheme, need to set the qualified Name to the
    // empty buffer to avoid correlation between keys
    if(CryptIsSchemeAnonymous(in->inScheme.scheme))
        certifyInfo.attested.certify.qualifiedName.t.size = 0;
    else
        certifyInfo.attested.certify.qualifiedName = certifiedObject->qualifiedName;


    // Sign attestation structure.  A NULL signature will be returned if
    // signHandle is TPM_RH_NULL.  A TPM_RC_NV_UNAVAILABLE, TPM_RC_NV_RATE,
    // TPM_RC_VALUE, TPM_RC_SCHEME or TPM_RC_ATTRIBUTES error may be returned
    // by SignAttestInfo()
    return SignAttestInfo(signObject, &in->inScheme, &certifyInfo,
                          &in->qualifyingData, &out->certifyInfo, &out->signature);
}

#endif // CC_Certify